import math
from math import degrees, radians

import pyglet
from pyglet.gl import  *
from pyglet.window import key

import cocos
from cocos.actions import *
from cocos.layer import *
from cocos.scene import *
from cocos.director import director
from cocos.scenes.transitions import *
from cocos.sprite import Sprite
from cocos.particle import *
from cocos.particle_systems import *

import Box2D as box2d


from res import Images
import entity
import vec2util
from globalvars import *


###--------------------------###       
#        
#    Weapon
#
###---------------------------### 

class  Weapon(entity.Entity):
    def __init__(self, world, box, image, *args, **kwargs):
        "Init"
        super(Weapon,self).__init__(world,image, *args,**kwargs)
        self.box = box
        self.isAddedToWorld = False
        self.maxAmmo = -1
        self.ammo = -1
        self.reloadAmmo = -1
        self.isReloading = False
        self.reloadTime = -1

        "BodyDef"
        self.body = None
        self.weaponShape = None
        
        
        
    def ownedTo(self,box):
        "Who?"
        self.box = box
    
    def update(self,dt):
        pass
    
    def onTrigger(self,dt):
        "on Trigger"
        pass
             
    def offTrigger(self,dt):
        "off Trigger"
        pass
    
    def reload(self,dt):
        "Reload! reload!"
        if self.reloadAmmo > 0:
            if self.ammo < self.maxAmmo:
                toBeReloaded = self.maxAmmo - self.ammo
                if toBeReloaded > self.reloadAmmo:
                    toBeReloaded = self.reloadAmmo
                self.ammo += toBeReloaded
                self.reloadAmmo -= toBeReloaded
                self.isReloading = False
                    
    def isBumpedSomething(self):
        "Hit something"
        shape_pairs = [(p.shape1, p.shape2) for p in self.world.points]
        for shape1, shape2 in shape_pairs:
             if (shape1 == self.weaponShape) or (shape2 == self.weaponShape):
                return True
        return False
    
    def isPickedUp(self):
        "Am I picked up?"
        if self.box == None:
            shape_pairs = [(p.shape1, p.shape2) for p in self.world.points]
            for shape1, shape2 in shape_pairs:
                if (shape1 == self.world.playerBox.boxShape) or (shape2 == self.world.playerBox.boxShape):
                    return self.world.playerBox
            return False
        
    def drop(self,dt=0):
        "Dropped"
        pass
    
    def throw(self,dt=0):
        "Thrown"
        pass
        
    def updateAnalogVecFunc(self,analogStick, x,y):
        "For use with the analog stick"
        pass
    
    def addedToWorld(self,flag=True):
        "Body and Shapes"
        if flag == True:
            if not self.body:
                self.body = self.world.CreateBody(self.bodyDef)
                self.weaponShape = self.body.CreateShape(self.weaponShapeDef)
                self.body.SetMassFromShapes()
                self.body.SetBullet(True)
                self.isAddedToWorld = True
 
        elif flag == False:
            if (self.body and self.weaponShape):
                self.world.DestroyBody(self.body)
                self.body = self.weaponShape = False
                self.isAddedToWorld = False
    
    
    class AsActiveWeapon(entity.State):
        def Enter(self,dt):
            self.ent.addedToWorld(True)
            self.ent.world.addEntity(self.ent)
    
    class AsInactiveWeapon(entity.State):
        def Enter(self,dt):
            print self
            self.ent.addedToWorld(False)
            self.ent.world.removeEntity(self.ent)
            
            
    class FreeForAnyone(entity.State): 
        def Enter(self,dt):
            self.ent.ownedTo(None)
            self.ent.addedToWorld(True)
            self.ent.world.addEntity(self.ent)
        
        def Execute(self,dt):
            self.ent.position = (self.ent.body.position * RATIO).tuple()
            self.ent.rotation = -(degrees(self.ent.body.angle))
            
    class AddedToABox(entity.State):
        def Enter(self,dt):
            pass
    
    class Dropped(entity.State):
        def Enter(self,dt):
            self.ent.stateMachine.changeState(self.ent.FreeForAnyone(self.ent))
    
    class Thrown(entity.State):
         def Enter(self,dt):
             self.ent.box = None
      
    
class Ammo(entity.Entity):
    def __init__(self, world, weapon, image=Images.semiautoriflebullet, *args, **kwargs):
        super(Ammo,self).__init__(world,image, *args,**kwargs)
        self.world = world
        self.weapon = weapon
        self.body = None
        self.ammoShape = None
    
    def update(self,dt):
        pass
        
class Magazine(Sprite):
    def __init__(self, world, box=None, image=Images.semiautorifle, *args, **kwargs):
        "Init"
        super(Magazine, self).__init__(image, *args,**kwargs)
        self.state = "unpicked"
        self.world = world
        self.box = box
        self.ammo = 0
        
        "BodyDef"
        self.body = None
        self.weaponShape = None

    def update(self,dt):
        if self.state == "unpicked":
            box =  self.isPickedUp()
            if box:
                pass
                
    def isPickedUp(self):
        "Am I picked up?"
        if self.box == None:
            shape_pairs = [(p.shape1, p.shape2) for p in self.world.points]
            for shape1, shape2 in shape_pairs:
                if (shape1 == self.world.playerBox.boxShape) or (shape2 == self.world.playerBox.boxShape):
                    return
                

###--------------------------###       
#        
#    NoWeapon
#
###---------------------------### 
            
class NoWeapon(Weapon):
    "Just a dummy for no weapon left - absolutely no box2d bodies and shapes"
    def __init__(self, world, box, image=Images.semiautorifle ,*args, **kwargs):
        "Init"
        super(NoWeapon,self).__init__(world, box, image, *args, **kwargs)
        self.opacity = 0 
        
    def addedToWorld(self,flag=True):
        pass
    
  

###--------------------------###       
#        
#    SemiAutoRifle
#
###---------------------------### 
            
class SemiAutoRifle(Weapon):
    "Release trigger to load next round"
    def __init__(self, world, box=None, image=Images.semiautorifle ,*args, **kwargs):
        "Init"
        super(SemiAutoRifle,self).__init__(world, box, image, *args, **kwargs)
        self.ammoVec = box2d.b2Vec2(0,0)
        self.margin = 1.8
        self.isShooting = False
        self.isRoundReady = False 
        self.maxAmmo = 10
        self.ammo = self.maxAmmo
        self.reloadAmmo = 60
        self.reloadTime = 2 # seconds
        
        "BodyDef"
        self.bodyDef = box2d.b2BodyDef()
        self.bodyDef.linearDamping = 0.6
        self.bodyDef.angularDamping = 0.6
        
        "ShapeDef"
        self.weaponShapeDef = box2d.b2PolygonDef()
        self.weaponShapeDef.SetAsBox(
                          self.width/RATIO/2.0, 
                         self.height/RATIO/2.0, 
                         (0,0), 0)
        self.weaponShapeDef.restitution = 0.4
        self.weaponShapeDef.friction = 0.3
        self.weaponShapeDef.density = 0.2
        
        self.weaponShapeDef.SetUserData(self)
        
        self.stateMachine.changeState(self.FreeForAnyone(self))
        
        
        
        #"Body and Shapes"
        #self.body = self.world.CreateBody(self.bodyDef)
        #self.weaponShape = self.body.CreateShape(self.weaponShapeDef)
        #self.body.SetMassFromShapes()
        #self.body.SetBullet(True)
    
    def update(self,dt):
        self.stateMachine.update(dt)   
        
        
 
    def onTrigger(self,dt):
        if self.isRoundReady:
            self.isShooting = True
        if self.isShooting:
             self.shoot(dt)
             
    def offTrigger(self,dt):
        if not self.isRoundReady:
            if self.ammo > 0:
                self.ammo -= 1
                self.isRoundReady = True
        
    
    def shoot(self,dt):
        bullet = SemiAutoRifleBullet(self.world, self, self.ammoVec, self.body.angle)
        bullet.color = self.color
        self.world.addEntity(bullet)
        self.isRoundReady = False
        self.isShooting = False
    
    def updateAnalogVecFunc(self,analogStick,x,y):
        playerBox = self.box
        if (vec2util.get_distance_to(analogStick.position, (x,y)) > ((0.6*(analogStick.width/2))*analogStick.scale)):
            pos = analogStick.position
            d = vec2util.normalize((pos[0] - x, pos[1] - y))
            dx = d[0]*analogStick.width/2
            dy = d[1]*analogStick.width/2
            analogStick.analogVec = box2d.b2Vec2(dx,dy)
            analogStick.isTriggered = True
        else:
            analogStick.analogVec =  box2d.b2Vec2(analogStick.position[0] - x, analogStick.position[1] - y)
            analogStick.isTriggered = False
            
    class AsActiveWeapon(Weapon.AsActiveWeapon):
        def Enter(self,dt):
            self.ent.addedToWorld(True)
            self.ent.world.addEntity(self.ent)
        
        def Execute(self,dt):
            #normTuple = vec2util.normalize(self.ent.box.weaponVec.tuple())
            #self.ent.ammoVec = -box2d.b2Vec2(normTuple[0],normTuple[1]) 
           # self.ent.body.position = self.ent.box.body.position + (self.ent.ammoVec * self.ent.margin)
           # self.ent.body.angle = self.ent.box.weaponAngle
            
            if self.ent.ammo <= 0:
                if not self.ent.isReloading:
                    pyglet.clock.schedule_once(self.ent.reload, self.ent.reloadTime)
                    self.ent.isReloading = True
                    
            self.ent.position = (self.ent.body.position * RATIO).tuple()
            self.ent.rotation = -(degrees(self.ent.body.angle))
                    
    class AsInactiveWeapon(Weapon.AsInactiveWeapon):
        def Enter(self,dt):
            if self.ent.isReloading:
                pyglet.clock.unschedule(self.ent.reload)
                self.ent.isReloading = False
            self.ent.addedToWorld(False)
            self.ent.world.removeEntity(self.ent)
         
        
        
        
    

class SemiAutoRifleBullet(Ammo):
    def __init__(self, world, weapon, vec=box2d.b2Vec2(0,0), angle=0, image=Images.semiautoriflebullet,*args, **kwargs):
        "semiAutoRifleBullet"
        super(SemiAutoRifleBullet,self).__init__(world, weapon, image, *args, **kwargs)
        self.state = "moving"
        self.speed = 50
        self.lifespan = 10# second
        self.velocityVec = vec
        
        "BodyDef"
        self.bodyDef = box2d.b2BodyDef()
        self.bodyDef.position = self.weapon.body.position + self.weapon.ammoVec * (self.weapon.margin/1.5)
        self.bodyDef.angle = angle
        self.bodyDef.linearDamping = 0
        self.bodyDef.angularDamping = 0.3
        
        "ShapeDef"
        self.ammoShapeDef = box2d.b2PolygonDef()
        self.ammoShapeDef.SetAsBox(
                          self.width/RATIO/2.0, 
                         self.height/RATIO/2.0, 
                         (0,0), 0)
        self.ammoShapeDef.restitution = 0.4
        self.ammoShapeDef.friction = 0.3
        self.ammoShapeDef.density = 0.7
        
        "Body and Shapes"
        self.body = self.world.CreateBody(self.bodyDef)
        self.ammoShape = self.body.CreateShape(self.ammoShapeDef)
        self.body.SetMassFromShapes()
        self.body.SetBullet(True)
        self.body.WakeUp()
        
        "Set pos"
        self.position = (self.body.position * RATIO).tuple()
        self.rotation = -(degrees(self.body.angle))
        
        "Set state"
        self.stateMachine.changeState(self.Move(self))
        
                    
    def update(self,dt):
        self.stateMachine.update(dt)
        self.position = (self.body.position * RATIO).tuple()
        self.rotation = -(degrees(self.body.angle))
    
    class Move(entity.State):
        def Enter(self,dt):
            self.ent.body.ApplyImpulse((self.ent.velocityVec * self.ent.speed), self.ent.body.position)
            #pyglet.clock.schedule_once(self.ent.stateMachine.changeState(self.ent.OutOfRange(self.ent)), 2)
    
        def Execute(self,dt):
            if self.ent.lifespan > 0:
                self.ent.lifespan -= 1
            if self.ent.lifespan <= 0:
                self.ent.stateMachine.changeState(self.ent.OutOfRange(self.ent))
                
                
    class OutOfRange(entity.State):
        def Enter(self,dt):
            self.ent.do(FadeOut(0.4) +  self.ent.RemoveSelf())
                
    class RemoveSelf( InstantAction ):
        def init(self):
            pass
        def start(self):
            self.target.world.remove(self.target)
            self.target.world.DestroyBody(self.target.body)

class SemiAutoRifleMagazine(Magazine):
    "The semi auto magazine"
    def __init__(self, world, box=None, image=Images.semiautoriflebullet, *args, **kwargs):
        "Init"
        super(SemiAutoRifleMagazine, self).__init__(world,box,image,*args,**kwargs)
        self.state = "unpicked"
        self.ammo = 15
        
###--------------------------###       
#        
#    Kuani
#
###---------------------------### 

class Kunai(Weapon):
    "Fallback weapon. Tactical ninja throwing knife - stab or slash or throw-and-pick-back-up"
    def __init__(self, world, box, image=Images.kunai ,*args, **kwargs):
        "Init"
        super(Kunai,self).__init__(world, box, image, *args, **kwargs)
        self.state = "held"
        self.ammoVec = box2d.b2Vec2(0,0)
        self.hasThrown = False
        self.speed = 10    
        self.margin = 2
        
        "BodyDef"
        self.bodyDef = box2d.b2BodyDef()
        self.bodyDef.linearDamping = 0.6
        self.bodyDef.angularDamping = 0.6
        
        "ShapeDef"
        self.weaponShapeDef = box2d.b2PolygonDef()
        self.weaponShapeDef.SetAsBox(
                          self.width/RATIO/2.0, 
                         self.height/RATIO/2.0, 
                         (0,0), 0)
        self.weaponShapeDef.restitution = 0.4
        self.weaponShapeDef.friction = 0.3
        self.weaponShapeDef.density = 0.2
        
        "body vars"
        self.body = None
        self.weaponShape = None
    
        
    def update(self,dt):
        #print self.box.weaponVec.tuple()
        if self.state == "held":
            normTuple = vec2util.normalize(self.box.weaponVec.tuple())
            self.ammoVec = -box2d.b2Vec2(normTuple[0],normTuple[1]) 
            
            self.body.position = self.box.body.position + (self.ammoVec * self.margin)
            self.body.angle = self.box.weaponAngle
            
        if self.state == "thrown":
            if self.isBumpedSomething():
                self.state = "dropped"
        
        if self.state == "dropped":
            box = self.isPickedUp()
            if box:
                box.addWeapon(self,"kunai")
                #self.addedToWorld(False)
            
        self.position = (self.body.position * RATIO).tuple()
        self.rotation = -(degrees(self.body.angle))

    def onTrigger(self,dt):
        pass
             
    def offTrigger(self,dt):
        pass

    def throw(self,dt):
        if self.box:
            normTuple = vec2util.normalize(self.box.weaponVec.tuple())
            self.ammoVec = box2d.b2Vec2(normTuple[0],normTuple[1])
            self.body.position = self.box.body.position + (-self.ammoVec * self.margin)
            self.body.WakeUp()
            self.body.ApplyImpulse((-self.ammoVec * self.speed), self.body.position)
            self.box = None
            self.state = "thrown"
            
    
    def updateAnalogVecFunc(self,analogStick, x,y):
        playerBox = self.box
        if (vec2util.get_distance_to(analogStick.position, (x,y)) > ((analogStick.width/2/3)*analogStick.scale)):
            pos = analogStick.position
            d = vec2util.normalize((pos[0] - x, pos[1] - y))
            dx = d[0]*analogStick.width/2
            dy = d[1]*analogStick.width/2
            analogStick.prevVec = box2d.b2Vec2(analogStick.analogVec.x,analogStick.analogVec.y)
            analogStick.analogVec = box2d.b2Vec2(dx,dy)
        else:
            analogStick.prevVec = box2d.b2Vec2(analogStick.analogVec.x,analogStick.analogVec.y)
            analogStick.analogVec = box2d.b2Vec2(analogStick.position[0] - x, analogStick.position[1] - y)
        
        dist = vec2util.get_distance_to(
        (analogStick.prevVec*playerBox.world.RATIO).tuple(), (analogStick.analogVec*playerBox.world.RATIO).tuple() )
        
        analogStick.isTriggered = True if (dist > 2500) else False
        if analogStick.isTriggered:
            analogStick.analogVec = analogStick.analogVec - analogStick.prevVec
            playerBox.throwWeapon()
            
